﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using KT.Mvvm;

namespace CloudClipboardSync
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            //var timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(500) };
            //timer.Tick += delegate { ProgramData.Instance.CreateNewSnapshot(); };
            //timer.IsEnabled = true;

            if(!GuiTools.IsDesignTime)
            {
                clipboardChangeListener = new ClipboardChangeListener();
                clipboardChangeListener.ClipboardChanged += delegate
                {
                    ProgramData.Instance.CreateNewSnapshot();
                };                
            }
        }

        private ClipboardChangeListener clipboardChangeListener;

        private void btnRefreshClick(object sender, RoutedEventArgs e)
        {
            //new Thread(() =>
            //{
            //    while(true)
            //    {
            //        Thread.Sleep(500);                    
            //    }
            //}) { IsBackground = true }.Start();
        }
    }

    public static partial class ExtensionMethods
    {
        public static void SafeInvoke(this EventHandler obj, object sender, EventArgs e)
        {
            obj.Invoke(sender, e);
        }
    }

    public static class GuiTools
    {
        public static void RunOnGuiThread(Action action)
        {
            var disp = Application.Current.Dispatcher;
            if(disp.CheckAccess())
                action();
            else
                disp.Invoke(action);
        }

        public static T RunOnGuiThread<T>(Func<T> function)
        {
            var disp = Application.Current.Dispatcher;
            if (disp.CheckAccess())
                return function();
            else
            {
                T result = default(T);
                disp.Invoke((Action)(() => { result = function(); }));
                return result;
            }
        }

        static GuiTools()
        {
            IsDesignTime = DesignerProperties.GetIsInDesignMode(new DependencyObject());            
        }

        public static bool IsDesignTime { get; private set; }
    }

    public static class Utils
    {
        public static string ProvidePath(string path)
        {
            new DirectoryInfo(path).Create();
            return path;
        }
    }

    public static partial class ExtensionMethods
    {
        public static PngBitmapEncoder ToPng(this BitmapSource image)
        {
            return new PngBitmapEncoder { Interlace = PngInterlaceOption.On, Frames = { BitmapFrame.Create(image) } };
        }        
    }

    public static class NullExtensions
    {
        public static TValue With<TObj, TValue>(this TObj obj, Func<TObj, TValue> getter) where TObj: class where TValue: class
        {
            return obj == null ? null : getter(obj);
        }

        public static void Do<TObj>(this TObj obj, Action<TObj> action) where TObj: class
        {
            if (obj != null)
                action(obj);
        }
    }

    public partial class ProgramData : MvvmBase
    {
        public static ProgramData Instance { get; protected set; }

        public ObservableCollection<ClipboardContent> ClipboardHistory { get; protected set; }
        public ClipboardContent SelectedClipboard { get; set; }

        public DateTime LastRefreshTime { get; protected set; }

        public bool NextCallDoesntMatter = false;

        public ProgramData()
        {
            Instance = this;
            //ClipboardItems = new ClipboardItem[0];
            ClipboardHistory = new ObservableCollection<ClipboardContent>();
            this.OnPropertyChanged(x => x.SelectedClipboard, () =>
            {
                NextCallDoesntMatter = true;
                SetClipboardContent(SelectedClipboard);
            });
        }

        public void CreateNewSnapshot()
        {
            if (NextCallDoesntMatter)
            {
                NextCallDoesntMatter = false;
                return;
            }

            LastRefreshTime = DateTime.Now;
            var path = Utils.ProvidePath(@"c:\dropbox\clipboard\" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss.ffff") + @"\");
            
            //RefreshClipboardItems();
            //if (ClipboardItems.Any())
            //    SaveClipboardItems(path);

            var content = GetCurrentContent();
            SaveContents(content, path);

            ClipboardHistory.Insert(0, content);
        }

        protected T GetItem<T>(string format = null) where T : class
        {
            if (format == null)
                format = typeof (T).FullName;

            var dataObj = Clipboard.GetDataObject();
            if(dataObj == null) return null;

            return dataObj.GetDataPresent(format) ? dataObj.GetData(format) as T : null;
            //return ClipboardItems.Where(x => x.Format == format).Select(x => x.Data as T).FirstOrDefault();
        }

        public const string HtmlFormatString = "HTML Format";
        public const string RichTextFormatString = "Rich Text Format";

        public ClipboardContent GetCurrentContent()
        {
            return new ClipboardContent
            {
                Image = GetItem<BitmapSource>(),
                Html = GetItem<string>(HtmlFormatString),
                Rtf = GetItem<string>(RichTextFormatString),
                Text = GetItem<string>(),
                Time = DateTime.Now,
            };
        }

        public void SetClipboardContent(ClipboardContent content)
        {
            var dataObj = new DataObject();
            
            if(content.Text != null) 
                dataObj.SetText(content.Text, TextDataFormat.Text);
            
            if(content.Rtf != null) 
                dataObj.SetText(content.Rtf, TextDataFormat.Rtf);
            
            if(content.Html != null)
                dataObj.SetText(content.Html, TextDataFormat.Html);

            if(content.Image != null) 
                dataObj.SetImage(content.Image);

            Clipboard.SetDataObject(dataObj);
        }

        public void SaveContents(ClipboardContent content, string path)
        {
            if (content.Image != null)
                using (var stream = File.Create(path + "image.png"))
                    content.Image.ToPng().Save(stream);

            if (content.Text != null)
                File.WriteAllText(path + "text.txt", content.Text);

            if (content.Rtf != null)
                File.WriteAllText(path + "text.rtf", content.Rtf);

            if (content.Html != null)
                File.WriteAllText(path + "text.html", content.Html);
        }
    }

    //public partial class ProgramData: MvvmBase
    //{
    //    public ClipboardItem[] ClipboardItems { get; protected set; }

    //    public void RefreshClipboardItems()
    //    {
    //        var dataObj = GuiTools.RunOnGuiThread(() => Clipboard.GetDataObject());
    //        if (dataObj == null) return;

    //        var newClipboardItems = dataObj.GetFormats().Select(format =>
    //        {
    //            var ci = new ClipboardItem { Format = format };

    //            try { ci.Data = dataObj.GetData(format); }
    //            catch (Exception e) { ci.DataConvertException = e; }

    //            return ci;
    //        }).ToArray();

    //        var same = newClipboardItems.Select(x => ClipboardItems.Any(y => y.ToString() == x.ToString())).ToArray();
    //        if (same.Any(x => x == false))
    //            ClipboardItems = newClipboardItems;
    //    }
    //}

    public class ClipboardContent
    {
        public DateTime Time { get; set; }

        public string Text { get; set; }
        public string Rtf { get; set; }
        public BitmapSource Image { get; set; }
        public string Html { get; set; }

        public string RtfOrText { get { return Rtf ?? Text; } }

        public void LoadImageFromStream(Stream stream)
        {
            Image = new PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default).Frames[0];
        }
    }

    public class ClipboardItem
    {
        public string Format { get; set; }
        
        public object Data { get; set; }
        public Exception DataConvertException { get; set; }

        public override string ToString()
        {
            var data = Data ?? DataConvertException;
            return String.Format("[{0} ({2})] {1}", Format, data, data == null ? "<null>" : data.GetType().ToString());
        }

        public override bool Equals(object obj)
        {
            return obj is ClipboardItem && Data == ((ClipboardItem)obj).Data;
        }
    }
}
